#ifndef __MQ_QUEUE_
#define __MQ_QUEUE_
#include "../mqcommon/mq_log.hpp"
#include "../mqcommon/mq_tools.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>

namespace MQ
{
    struct MsgQueue
    {
        using ptr = std::shared_ptr<MsgQueue>;

        std::string name;
        bool durable;
        bool exclusive;
        bool auto_delete;
        google::protobuf::Map<std::string, std::string> args;

        MsgQueue()
        {
        }

        MsgQueue(std::string &qname, bool qdurable, bool qexclusive, bool qauto_delete, const google::protobuf::Map<std::string, std::string> &qargs)
            : name(qname), durable(qdurable), exclusive(qexclusive), auto_delete(qauto_delete), args(qargs)
        {
        }

        void setArgs(const std::string &str)
        {
            std::vector<std::string> result;
            SplitHelper::split(str, "&", result);
            for (auto e : result)
            {
                size_t pos = e.find("=");
                std::string key = e.substr(0, pos);
                std::string val = e.substr(pos + 1);
                args[key] = val;
            }
        }

        std::string getArgs()
        {
            std::string result;
            for (auto e : args)
                result += e.first + "=" + e.second + "&";

            return result;
        }
    };

    using QueueMap = std::unordered_map<std::string, MsgQueue::ptr>;
    class QueueMapper
    {
    public:
        QueueMapper(const std::string &dbfile)
            : _sql_helper(dbfile)
        {
            std::string path = FileHelper::parentDir(dbfile);
            bool ret = FileHelper::createDirectory(path);
            assert(_sql_helper.open());
            CreateTable();
        }

        bool insert(MsgQueue::ptr &msp)
        {
            std::stringstream ss;
            ss << "insert into queue_table values(";
            ss << "'" << msp->name << "', ";
            ss << msp->durable << ", ";
            ss << msp->exclusive << ", ";
            ss << msp->auto_delete << ", ";
            ss << "'" << msp->getArgs() << "');";
            return _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        void delete_queue(const std::string &name)
        {
            std::stringstream ss;
            ss << "delete from queue_table where name=";
            ss << "'" << name << "';";
            _sql_helper.exec(ss.str(), nullptr, nullptr);
        }

        void delete_table()
        {
            std::string sql = "drop table if exists queue_table;";
            _sql_helper.exec(sql, nullptr, nullptr);
        }

        QueueMap recovery()
        {
            std::string sql = "select name, durable, exclusive, auto_delete, args from queue_table;";
            std::unordered_map<std::string, MsgQueue::ptr> result;
            _sql_helper.exec(sql, selectCallback, &result);
            return result;
        }

    private:
        void CreateTable()
        {
            std::string sql = "create table if not exists queue_table (name varchar(32) primary key, durable int, exclusive int, auto_delete int, args varchar(128));";
            bool ret = _sql_helper.exec(sql, nullptr, nullptr);
            if (ret == false)
            {
                LOG(ERROR, "Create table fail!!!\n");
                abort();
            }
        }

        static int selectCallback(void *args, int numcol, char **row, char **fields)
        {
            QueueMap *result = static_cast<QueueMap *>(args);
            MsgQueue::ptr msp = std::make_shared<MsgQueue>();
            msp->name = row[0];
            msp->durable = static_cast<bool>(std::stoi(row[1]));
            msp->exclusive = static_cast<bool>(std::stoi(row[2]));
            msp->auto_delete = static_cast<bool>(std::stoi(row[3]));
            if (row[4])
                msp->setArgs(row[4]);
            result->insert(std::make_pair(msp->name, msp));
            return 0;
        }

    private:
        SqliteHelper _sql_helper;
    };

    class QueueManager
    {
    public:
        using ptr = std::shared_ptr<QueueManager>;

        QueueManager(const std::string &dbfile)
            : _mapper(dbfile)
        {
            _msg_queues = _mapper.recovery();
        }

        // 声明
        bool declareQueue(const std::string &qname, bool qdurable, bool qexclusive, bool qauto_delete, const google::protobuf::Map<std::string, std::string> &qargs)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            auto it = _msg_queues.find(qname);
            if (it != _msg_queues.end())
                return true;

            // 构建Queue
            MsgQueue::ptr msp = std::make_shared<MsgQueue>();
            msp->name = qname;
            msp->durable = qdurable;
            msp->exclusive = qexclusive;
            msp->auto_delete = qauto_delete;
            msp->args = qargs;

            // 判断是否需要持久化
            if (msp->durable)
            {
                bool ret = _mapper.insert(msp);
                if (ret == false)
                {
                    LOG(ERROR, "insert fail!!!\n");
                    return false;
                }
            }

            // map中管理
            // 声明队列成功
            LOG(DEBUG, "声明队列%s成功\n", msp->name.c_str());
            _msg_queues.insert(std::make_pair(msp->name, msp));
            return true;
        }

        // 删除
        void deleteQueue(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            if (it == _msg_queues.end())
                return;

            if (it->second->durable == true)
                _mapper.delete_queue(it->first);

            _msg_queues.erase(name);
        }

        // 获取
        MsgQueue::ptr selectQueue(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            if (it == _msg_queues.end())
            {
                LOG(WARNING, "不存在该队列!!!\n");
                return MsgQueue::ptr();
            }
            return it->second;
        }

        // 存在
        bool exists(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            auto it = _msg_queues.find(name);
            if (it == _msg_queues.end())
                return false;
            else
                return true;
        }

        // 所有Queue
        QueueMap allQueue()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _msg_queues;
        }

        // 清空
        void clear()
        {
            _mapper.delete_table();
            _msg_queues.clear();
        }

        // 测试size
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _msg_queues.size();
        }

    private:
        std::mutex _mutex;
        QueueMap _msg_queues;
        QueueMapper _mapper;
    };

}
#endif